import { Ref } from "vue";
import dayjs from "dayjs";
import { IDragData, IScheduleResource, IScheduleTask } from "../interface";
import useStore from "../store";
import { useCommonAbility } from "./use-schedule-table";

export function useDrag(
  props: any,
  coordinateElement: Ref<HTMLDivElement | null>,
  bodyCanvas: Ref<HTMLCanvasElement | null>
): {
  handleDragOver: (payload: DragEvent) => void;
  handleDragLeave: (payload: DragEvent) => void;
  handleDrop: (payload: DragEvent) => Promise<void>;
  handleDragStart: (
    payload: DragEvent,
    dataId: string,
    task: IScheduleTask
  ) => void;
} {
  const { $renderLayer, $uiCoordinate, $config } = useStore();

  const config = $config.getConfig();

  const resources = $renderLayer.getResources();

  const { refresh } = useCommonAbility(coordinateElement);

  /**
   * @description 根据拖拽事件源计算位置
   * @param {DragEvent} payload
   * @returns {*}  {{
   *     date: Date;
   *     resourceId: IScheduleResource;
   *     position: { x: number; y: number };
   *   }}
   */
  const calcPositionByEvent = (
    payload: DragEvent
  ): {
    date: dayjs.Dayjs;
    resource: IScheduleResource;
    position: { x: number; y: number };
  } => {
    const rect = bodyCanvas.value!.getBoundingClientRect();
    const {
      startTime,
      scaleRange,
      dragInterval,
      scaleColumnWidth,
      resourceColumnWidth,
      resourceBodyRowHeight,
    } = config;
    const x =
      payload.clientX - rect.left - resourceColumnWidth - scaleColumnWidth;
    const y = payload.clientY - rect.top;
    const resourceIndex = Math.ceil(y / resourceBodyRowHeight) - 1;
    // 资源
    const resource = resources[resourceIndex];
    // 天索引
    const dayIndex = Math.ceil(x / $uiCoordinate.cellWidth) - 1;
    // 刻度开始时间
    const startDate = dayjs(startTime)
      .add(dayIndex, "day")
      .startOf("day")
      .hour(scaleRange[0]);
    const range = scaleRange[1] - scaleRange[0];
    // 拖拽精度的高度（使用分钟来计算）
    const intervalHeight =
      resourceBodyRowHeight / ((range * 60) / (dragInterval * 60));
    const height = y - resourceIndex * config.resourceBodyRowHeight;
    const date = startDate.add(Math.round(height / intervalHeight), "hour");
    return { position: { x, y }, date, resource };
  };

  /**
   * @description 处理拖拽经过
   * @param {DragEvent} payload
   */
  const handleDragOver = (payload: DragEvent): void => {
    payload.preventDefault();
    const element = document.querySelector("#drag-ghost-date");
    if (element) {
      const { date, position } = calcPositionByEvent(payload);
      element.innerHTML = position.x > 0 ? date.format("YYYY-MM-DD HH:mm") : "";
    }
  };

  /**
   * @description 处理拖拽离开
   * @param {DragEvent} payload
   */
  const handleDragLeave = (payload: DragEvent): void => {
    const element = document.querySelector("#drag-ghost-date");
    if (element) element.innerHTML = "";
  };

  /**
   * @description 处理拖拽放置
   * @param {DragEvent} payload
   * @returns {*}
   */
  const handleDrop = async (payload: DragEvent): Promise<void> => {
    payload.preventDefault();
    if (!coordinateElement.value || !bodyCanvas.value) return;
    try {
      const str = payload.dataTransfer?.getData("data");
      if (!str) return;
      const dragData: IDragData = JSON.parse(str);
      const { dragType, data, scheduleType, dataId } = dragData;
      if (!dragType || !["resource", "task"].includes(scheduleType)) return;

      if (scheduleType === "resource") {
        const result = await props.beforeResourceDrop(data);
        if (result)
          $renderLayer.updateResource({
            data,
            tasks: [],
            id: dataId,
            name: data.name,
          });
      } else {
        const { position, date, resource } = calcPositionByEvent(payload);
        // 任务未拖拽到任务区或时长为零时不做处理
        // TODO: 有些任务又时长，有些任务没有时长，有开始时间和结束时间，
        let duration = data.planned_duration
          ? Number(data.planned_duration)
          : 0;
        // TODO 临时测试时长默认为2小时
        duration = 120;
        if (position.x < 0 || duration === 0) return;
        const start = new Date(date.format("YYYY-MM-DD HH:mm"));
        const end = new Date(
          date.add(duration, "minute").format("YYYY-MM-DD HH:mm")
        );
        const result = await props.beforeTaskDrop(data, resource, {
          startTime: start,
          endTime: end,
        });
        if (result)
          $renderLayer.updateTask({
            end,
            start,
            id: dataId,
            name: data.name,
            resourceId: resource.id,
            data: dragType === "add" ? data : data.data,
          });
      }
      // 更新绘制
      refresh();
    } catch (error) {
      ibiz.log.error(error);
    }
  };

  /**
   * @description 处理开始拖拽
   * @param {DragEvent} payload
   * @param {string} dataId
   * @param {IScheduleTask} task
   */
  const handleDragStart = (
    payload: DragEvent,
    dataId: string,
    task: IScheduleTask
  ): void => {
    payload.dataTransfer!.effectAllowed = "copy";
    const item: IDragData = {
      dataId,
      data: task,
      dragType: "update",
      scheduleType: "task",
    };
    payload.dataTransfer!.setData("data", JSON.stringify(item));
  };

  return { handleDrop, handleDragOver, handleDragStart, handleDragLeave };
}
